<?php
/**
 * 缩略图处理
 *
 * @package    classes
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 */
class Thumb
{

    /**
     * 用于存储图片数据信息
     *
     * @var array
     */
    protected $_info;

    /**
     * 构造方法
     *
     * @param string $source
     */
    public function __construct($source = NULL)
    {
        if (!is_null($source))
        {
            $this->set_source($source);
        }
    }

    /**
     * 设定图片源文件
     *
     * @param  string  $source
     * @return Thumb
     * @throws Exception
     */
    public function set_source($source)
    {
        if ( ! is_readable($source))
        {
            throw new Exception('图片源文件不存在或无法加载：'.$source);
        }

        $this->_info['orig_image'] = $source;
        $this->_info['orig_size']  = @getimagesize($source);
        switch ($this->_info['orig_size'][2])
        {
            case 1  : $this->_info['im'] = imagecreatefromgif ($source); break; /*gif*/
            case 2  : $this->_info['im'] = imagecreatefromjpeg($source); break; /*jpg*/
            case 3  : $this->_info['im'] = imagecreatefrompng ($source); break; /*png*/
            case 6  : $this->_info['im'] = imagecreatefrombmp ($source); break; /*bmp*/
            case 15 : $this->_info['im'] = imagecreatefromwbmp($source); break; /*wbmp*/
            default :  throw new Exception('不支持的图片文件类型');
        }

        return $this;
    }

    /**
     * 生成图片缩略图
     *
     * @param  int   $max_w  最大宽度
     * @param  int   $max_h  最大高度
     * @param  bool  $const  是否约束图片长宽比
     * @return Thumb
     */
    public function make($max_w, $max_h = FALSE, $const = TRUE)
    {
        $orig_size = & $this->_info['orig_size'];
        $im = & $this->_info['im'];
        $resize_h = $resize_w = FALSE;

        $orig_size[0] > $max_w AND $max_w AND $resize_w = TRUE;
        $orig_size[1] > $max_w AND $max_h AND $resize_h = TRUE;

        if ($resize_h AND $resize_w)
        {
            $resize_h = ($orig_size[0] / $max_w < $orig_size[1] / $max_h);
            $resize_w = ! $resize_h;
        }
        if ($resize_w)
        {
            if ($const)
            {
                $new_w = $max_w;
                $new_h = ($orig_size[1] * $max_w) / $orig_size[0];
            }
            else
            {
                $new_w = $max_w;
                $new_h = $orig_size[1];
            }
        }
        elseif ($resize_h)
        {
            if ($const)
            {
                $new_w = ($orig_size[0] * $max_h) / $orig_size[1];
                $new_h = $max_h;
            }
            else
            {
                $new_w = $orig_size[0];
                $new_h = $max_h;
            }
        } else {
            $new_w = $orig_size[0];
            $new_h = $orig_size[1];
        }

        if ($new_w != $orig_size[0] || $new_h != $orig_size[1]) {
            $im_n = imagecreatetruecolor($new_w, $new_h);
            imagecopyresampled($im_n, $im, 0, 0, 0, 0, $new_w, $new_h, $orig_size[0], $orig_size[1]);
            imagedestroy($im);
            $this->_info['im'] = $im_n;
        }

        $this->_info['orig_size'][0] = $new_w;
        $this->_info['orig_size'][1] = $new_h;

        return $this;
    }

    /**
     * 按指定区域大小截取图片
     *
     * @param  int  $start_x  开始 X 轴坐标
     * @param  int  $start_y  开始 Y 轴坐标
     * @param  int  $end_x    结束 X 轴坐标
     * @param  int  $end_y    结束 Y 轴坐标
     * @return Thumb
     */
    public function crop($start_x, $start_y, $end_x = FALSE, $end_y = FALSE)
    {
        $im = & $this->_info['im'];
        $orig_size = & $this->_info['orig_size'];

        $end_x == FALSE AND $end_x = $orig_size[0] - $start_x;
        $end_y == FALSE AND $end_y = $orig_size[1] - $start_y;

        $width  = $end_x - $start_x;
        $height = $end_y - $start_y;

        $im_n = imagecreatetruecolor($width, $height);
        imagecopy($im_n, $im, 0, 0, $start_x, $start_y, $width, $height);
        imagedestroy($im);

        $this->_info['im'] = $im_n;
        $this->_info['orig_size'][0] = $width;
        $this->_info['orig_size'][1] = $height;

        return $this;
    }

    /**
     * 按指定尺寸截取图片，以中心为起点
     *
     * @param  int  $width   图片宽度
     * @param  int  $height  图片高度
     * @param  int  $move_x  X 轴坐标偏移像素
     * @param  int  $move_y  Y 轴坐标偏移像素
     * @return Thumb
     */
    public function crop_center($width, $height, $move_x = 0, $move_y = 0)
    {
        $orig_size = & $this->_info['orig_size'];

        $center_x  = $orig_size[0] / 2;
        $center_y  = $orig_size[1] / 2;

        $top_x = $center_x - $width / 2;
        $top_y = $center_y - $height / 2;

        $end_x = $center_x + $width / 2;
        $end_y = $center_y + $height / 2;

        $this->crop($top_x + $move_x, $top_y + $move_y, $end_x + $move_x, $end_y + $move_y);

        return $this;
    }

    /**
     * 创建目录(递归创建)
     *
     * @param  string  $path
     * @return string|false
     */
    protected function _mkdir($dir)
    {
        $dir = preg_replace('/[\\\\\/]+/', '/', (string) $dir);

        if ( ! is_dir($dir))
        {
            $mk = @mkdir($dir, 0777, TRUE);
            if ($mk === FALSE)
            {
                return FALSE;
            }
        }

        return $dir;
    }

    /**
     * 创建图片文件，如果存在则自动覆盖
     *
     * @param  string   $output   输出的图片文件名及路径
     * @param  string   $as       输出格式 (gig, wbmp, jpeg)
     * @param  int      $quality  图片质量 (0-100)，jpeg 格式下有效
     * @return bool
     * @throws Exception
     */
    public function build($output = NULL, $as = NULL, $quality = 100)
    {
        if (is_null($output))
        {
            $output = $this->_info['orig_image'];
        }
        if ( ! is_null($as))
        {
            $as = strtolower(substr(strrchr($output, '.'), 1));
        }

        if ( ! $this->_mkdir(dirname($output)))
        {
            throw new Exception('无法创建缩略图存储目录：' . dirname($output));
        }

        switch ($as)
        {
            case 'bmp'  : $image =  @imagebmp ($this->_info['im'], $output); break;
            case 'wbmp' : $result = @imagewbmp($this->_info['im'], $output); break;
            case 'gif'  : $result = @imagegif ($this->_info['im'], $output); break;
            case 'png'  : $result = @imagepng ($this->_info['im'], $output); break;
            default     : $result = @imagejpeg($this->_info['im'], $output, $quality);
        }
        return $result ? $result : FALSE;
    }

    /**
     * 输出缩略图
     *
     * @param  bool     $build    是否创建缩略图文件
     * @param  string   $output   输出的图片文件名及路径
     * @param  string   $as       输出格式 (gig, wbmp, jpeg)
     * @param  int      $quality  图片质量 (0-100)，jpeg 格式下有效
     */
    public function output($build = FALSE, $output = NULL, $as = NULL, $quality = 100)
    {
        $mime = $this->_info['orig_size']['mime'];
        $build AND $this->build($output, $as, $quality);
        header("Content-type: $mime");
        switch ($mime)
        {
            case 'bmp'  : $image = imagebmp ($this->_info['im']); break;
            case 'wbmp' : $image = imagewbmp($this->_info['im']); break;
            case 'gif'  : $image = imagegif ($this->_info['im']); break;
            case 'png'  : $image = imagepng ($this->_info['im']); break;
            default     : $image = imagejpeg($this->_info['im']);
        }
    }

    /**
     * 给图片加水印图
     *
     * @param  string   $waterimg 水印图片地址
     */
    public function createwater($waterimg)
    {
    	if ( ! is_file($waterimg))
        {
            throw new Exception('水印图片不存在');
        }

    	$wimginfo  = @getimagesize($waterimg);

    	if ($this->_info['orig_size'][0] <= 500) return $this;
        switch ($wimginfo[2])
        {
            case 1  : $wimg = imagecreatefromgif ($waterimg); break; /*gif*/
            case 2  : $wimg = imagecreatefromjpeg($waterimg); break; /*jpg*/
            case 3  : $wimg = imagecreatefrompng ($waterimg); break; /*png*/
            case 6  : $wimg = imagecreatefrombmp ($waterimg); break; /*bmp*/
            case 15 : $wimg = imagecreatefromwbmp($waterimg); break; /*wbmp*/
            default :  throw new Exception('不支持的水印图片类型');
        }

        imagecopy($this->_info['im'],
        		  $wimg,
        		  (int) (($this->_info['orig_size'][0] - $wimginfo[0] ) / 2 ),
        		  (int) (($this->_info['orig_size'][1] - $wimginfo[1] ) / 2 ),
        		  0,
        		  0,
        		  $wimginfo[0],
        		  $wimginfo[1]);
        imagedestroy($wimg);

        return $this;
    }
}

if ( ! function_exists('imagebmp'))
{
    /**
     *  以 BMP 格式将图像输出到浏览器或文件
     *
     * @param  resource  $image     图像资源
     * @param  string    $filename  如果要另存为文件，请指定文件名，为空则直接在浏览器输出
     * @return bool
     */
    function imagebmp( & $im, $filename = NULL)
    {
        if ( ! $im)
        {
            return FALSE;
        }
        $w = imagesx($im);
        $h = imagesy($im);
        $result = '';

        if ( ! imageistruecolor($im))
        {
            $tmp = imagecreatetruecolor($w, $h);
            imagecopy($tmp, $im, 0, 0, 0, 0, $w, $h);
            imagedestroy($im);
            $im = & $tmp;
        }

        $biBPLine = $w * 3;
        $biStride = ($biBPLine + 3) & ~3;
        $biSizeImage = $biStride * $h;
        $bfOffBits = 54;
        $bfSize = $bfOffBits + $biSizeImage;

        $result .= substr('BM', 0, 2);
        $result .= pack('VvvV', $bfSize, 0, 0, $bfOffBits);
        $result .= pack('VVVvvVVVVVV', 40, $w, $h, 1, 24, 0, $biSizeImage, 0, 0, 0, 0);

        $numpad = $biStride - $biBPLine;
        for ($y = $h - 1; $y >= 0; --$y)
        {
            for ($x = 0; $x < $w; ++$x)
            {
                $col = imagecolorat($im, $x, $y);
                $result .=  substr(pack ('V', $col), 0, 3);
            }
            for ($i = 0; $i < $numpad; ++$i)
            {
                $result .= pack ('C', 0);
            }
        }

        if (NULL == $filename)
        {
            header("Content-Type: image/bmp");
            echo $result;
        }
        else
        {
            $file = fopen($filename, "wb");
            fwrite($file, $result);
            fclose($file);
        }

        return TRUE;
    }

}

if ( ! function_exists('imagecreatefrombmp'))
{
    /**
     * 从 BMP 文件或 URL 新建一图像
     *
     * @param  string  $filename
     * @return resource
     */
    function imagecreatefrombmp($filename)
    {
        if ( ! ($f1 = fopen($filename, "rb")))
        {
            return FALSE;
        }

        $file = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1, 14));
        if ($file['file_type'] != 19778)
        {
            return FALSE;
        }

        $bmp = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'.
                      '/Vcompression/Vsize_bitmap/Vhoriz_resolution'.
                      '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1, 40));

        $bmp['colors'] = pow(2, $bmp['bits_per_pixel']);
        if ($bmp['size_bitmap'] == 0)
        {
            $bmp['size_bitmap'] = $file['file_size'] - $file['bitmap_offset'];
        }
        $bmp['bytes_per_pixel'] = $bmp['bits_per_pixel'] / 8;
        $bmp['bytes_per_pixel2'] = ceil($bmp['bytes_per_pixel']);
        $bmp['decal'] = ($bmp['width']*$bmp['bytes_per_pixel'] / 4);
        $bmp['decal'] -= floor($bmp['width'] * $bmp['bytes_per_pixel'] / 4);
        $bmp['decal'] = 4 - ( 4 * $bmp['decal']);

        if ($bmp['decal'] == 4)
        {
            $bmp['decal'] = 0;
        }

        $palette = array();
        if ($bmp['colors'] < 16777216)
        {
            $palette = unpack('V' . $bmp['colors'], fread($f1, $bmp['colors'] * 4));
        }

        $img = fread($f1, $bmp['size_bitmap']);
        $vide = chr(0);
        $res = imagecreatetruecolor($bmp['width'], $bmp['height']);
        $p = 0;
        $y = $bmp['height'] - 1;

        while ($y >= 0)
        {
            $x = 0;
            while ($x < $bmp['width'])
            {
                if ($bmp['bits_per_pixel'] == 24)
                {
                    $color = unpack('V', substr($img, $p, 3) . $vide);
                }
                elseif ($bmp['bits_per_pixel'] == 16)
                {
                    $color = unpack('n', substr($img, $p, 2));
                    $color[1] = $palette[$color[1] + 1];
                }
                elseif ($bmp['bits_per_pixel'] == 8)
                {
                    $color = unpack('n', $vide . substr($img, $P, 1));
                    $color[1] = $palette[$color[1] + 1];
                }
                elseif ($bmp['bits_per_pixel'] == 4)
                {
                    $color = unpack("n", $vide . substr($img, floor($P), 1));
                    if (($P * 2) % 2 == 0)
                    {
                        $color[1] = ($color[1] >> 4);
                    }
                    else
                    {
                        $color[1] = ($color[1] & 0x0F);
                    }
                    $color[1] = $palette[$color[1] + 1];
                }
                elseif ($bmp['bits_per_pixel'] == 1)
                {
                    $color = unpack("n", $vide . substr($img, floor($P), 1));
                        if (($P * 8) % 8 == 0) $color[1] =  $color[1]         >> 7;
                    elseif (($P * 8) % 8 == 1) $color[1] = ($color[1] & 0x40) >> 6;
                    elseif (($P * 8) % 8 == 2) $color[1] = ($color[1] & 0x20) >> 5;
                    elseif (($P * 8) % 8 == 3) $color[1] = ($color[1] & 0x10) >> 4;
                    elseif (($P * 8) % 8 == 4) $color[1] = ($color[1] & 0x8 ) >> 3;
                    elseif (($P * 8) % 8 == 5) $color[1] = ($color[1] & 0x4 ) >> 2;
                    elseif (($P * 8) % 8 == 6) $color[1] = ($color[1] & 0x2 ) >> 1;
                    elseif (($P * 8) % 8 == 7) $color[1] = ($color[1] & 0x1 );
                    $color[1] = $palette[$color[1] + 1];
                }
                else
                {
                    return FALSE;
                }
                imagesetpixel($res, $x, $y, $color[1]);
                $x++;
                $p += $bmp['bytes_per_pixel'];
            }
            $y--;
            $p += $bmp['decal'];
        }

        fclose($f1);

        return $res;
    }
}
